- @title = 'Form manipulation'
:textile
  h3. Form manipulation
  
  As of version 0.2, Ojay includes a package called @Ojay.Forms@. This package provides a
  number of useful classes and APIs for dealing with forms: it allows for easy serialization
  and submission using Ajax; it allows radio buttons, checkboxes and drop-down menus to
  be styled using CSS, and it provides a validation mini-language for checking data before
  it is sent to the server.
  
  h3. Required files
  
  * @http://yui.yahooapis.com/2.6.0/build/yahoo-dom-event/yahoo-dom-event.js@
  * @http://yui.yahooapis.com/2.6.0/build/selector/selector-beta.js@
  * @http://yoursite.com/ojay/js-class.js@
  * @http://yoursite.com/ojay/core.js@
  * @http://yoursite.com/ojay/pkg/forms.js@
  
  h3. Examples
  
  * "Visual styling and event handling":/examples/forms.html
  * "Data validation using the @Ojay.Forms@ DSL":/examples/validation.html
  
  h3. Utility functions
  
  First off, the small stuff. @Ojay.Forms@ has a few utility methods that help when dealing
  with forms. They are as follows:
  
  * @Ojay.Forms.getLabel(input)@ - this returns an Ojay collection wrapping the @label@
    tag associated with the given form @input@, which can be a CSS selector string or an
    element reference. It knows about labels as parent elements and about the @for@ attribute
    and makes dealing with labels really simple.
  * @Ojay.Forms.getQueryString(form)@ - returns the form's current data as a query string.
    @form@ can be a CSS selector string or an element reference.
  * @Ojay.Forms.getData(form)@ - the same as @getQueryString()@, except that it returns
    the data as an object where the property names mirror the form's input names.
  
  h3. Visual styling
  
  @Ojay.Forms@ includes a set of classes that can be used to replace unstylable form elements
  with HTML you can style with CSS. These classes are designed to be lightweight, unobtrusive
  replacements for the @YAHOO.widget.Button@ family of classes for the basic case where you
  just want to style form inputs. @Ojay@'s input replacements do not rely on extra button
  elements and hidden fields - they simply hide the original inputs and use class names on
  @label@ tags to allow styling using CSS. As the fundamental HTML structure of the form is
  left untouched, you can still read data out of the form much more easily than you can
  using the @YAHOO@ classes.
  
  Setting up checkboxes and radio buttons is straightforward. Just set up a form that works
  without JavaScript, and associate a @label@ with each form element. Then make a couple
  of JavaScript calls and have your inputs transformed. See the "example implementation":/examples/forms.html
  for more details on CSS class names and the like.
  
  <pre class="prettyprint">    <form action="/" method="post" id="the-form">
        <div class="radios">
          <input type="radio" name="cc" value="visa"
              id="cc_visa" />
          <label for="cc_visa">Visa</label>
          
          <input type="radio" name="cc" value="mastercard"
              id="cc_mastercard" />
          <label for="cc_mastercard">Mastercard</label>
          
          <input type="radio" name="cc" value="amex"
              id="cc_amex" />
          <label for="cc_amex">AmEx</label>
          
          <input type="radio" name="cc" value="switch"
              id="cc_switch" />
          <label for="cc_switch">Switch</label>
        </div>
        <input type="checkbox" name="accept" id="accept" value="1" /> 
        <label for="accept">Accept terms
            and conditions?</label>
      </form></pre>
  
  And the script:
  
  <pre class="prettyprint">    new Ojay.Forms.RadioButtons('.radios input');
      new Ojay.Forms.Checkbox('#accept');</pre>
  
  @Ojay@ 'hides' the original form inputs by wrapping them in a @span@ and using positioning,
  for example:
  
  <pre class="prettyprint">    <span style="position: relative;">
        <input type="checkbox" name="foo"
            style="position: absolute; left: -5000px; top: 0;" />
      </span></pre>
  
  This is designed to preserve keyboard navigability by keeping the inputs at their original
  vertical place in the document; using @display: none@ stops you from selecting the inputs
  using the keyboard. This works well in most situations but sometimes setting @position: absolute@
  on the span works better. To do this, pass the @wrapperPosition@ option when setting up
  an @Ojay.Forms@ object:
  
  <pre class="prettyprint">    new Ojay.Forms.Checkbox('#foo', {wrapperPosition: 'absolute'});</pre>
  
  @select@ elements are similarly easy to style. Just add a @select@ with a @label@ to
  your form and make a quick JavaScript call as follows:
  
  <pre class="prettyprint">    <label for="my-select">Delivery method</label>
      <select id="my-select">
        <option value="null">Choose...</option>
        <option value="first-class">First class post</option>
        <option value="recorded-delivery">Recorded delivery</option>
        <option value="air-mail">Air mail</option>
      </select></pre>
  
  with this script:
  
  <pre class="prettyprint">
      var selector = new Ojay.Forms.Select('#my-select');</pre>
  
  This will insert the following markup after the @select@ tag in your document:
  
  <pre class="prettyprint">    <div class="select-container">
        <div class="select-display">Choose...</div>
        <div class="select-button"></div>
        <div class="select-list">
          <ul>
            <li class="hovered">Choose...</li>
            <li>First class post</li>
            <li>Recorded delivery</li>
            <li>Air mail</li>
          </ul>
        </div>
      </div></pre>
  
  The list positioning is handled for you by @Ojay@, and you can take care of all the
  presentation using CSS. We recommend that you do not add borders or padding to the
  @select-container@ or @select-list@ elements, but apply such styling to @select-display@
  and the @ul@ element instead. This will make sure the positioning is handled correctly
  and still gives you room to style the menu. The @hovered@ class on the @li@ element
  will move between elements as the user interacts with the control. Again, see the
  "example implementation":/examples/forms.html for more ideas.
  
  All @label@ elements and @select-container@ elements receive a selection of CSS
  classes as the user interacts with the form and these classes can be used in your
  stylesheet to change appearance. The classes are:
  
  * @hovered@ - applied when the mouse is over the element
  * @focused@ - applied when the (hidden) original form element has focus
  * @disabled@ - applied when an element is disabled
  * @checked@ - for radios and checkboxes, applied when the element is checked
  
  h3. Changing values and handling events
  
  The @Ojay.Forms.Checkbox@, @Ojay.Forms.RadioButtons@ and @Ojay.Forms.Select@ classes
  all support a common API for getting and setting form values and handling events.
  All types of input publish a @change@ event when their value is changed, and you
  can listen to such events and respond to them:
  
  <pre class="prettyprint">    // formInput is a Checkbox, RadioButtons or Select instance
      formInput.on('change', function(input) {
        var currentValue = input.getValue();
        // do something with value
      });</pre>
  
  The callback will always be passed a reference to the @Forms.*@ object that triggered
  the event. The @Forms@ classes all have a @getValue()@ method, which returns the
  value of the currently selected option (or @true@ or @false@ for checkboxes). They
  also have a @setValue()@ method. This will select the option with the given value
  if such an option exists. For checkboxes, pass @true@ or @false@ to set the state
  of the box.
  
  <pre class="prettyprint">    // for radio button groups and select boxes...
      group.setValue('some-value');
      
      // for checkboxes...
      checkbox.setValue(true);</pre>
  
  By default, @setValue()@ will trigger a @change@ event as described above if it
  changes the value of the input. To suppress the event from firing, pass @false@
  as the second argument to @setValue()@.
  
  <pre class="prettyprint">    // don't alert listeners to the change in value
      radioButtons.setValue('the-value', false);</pre>
  
  Finally, @Checkbox@, @RadioButtons@ and @Select@ instances have two methods for
  accessing their underlying DOM elements:
  
  * @getInput()@ returns an Ojay collection wrapping the input element(s) (plural
    for radio buttons) belonging to the instance.
  * @getLabel()@ returns an Ojay collection wrapping the label element(s) belonging
    to the instance.
  
  h3. Validation API
  
  The final aspect of the @Forms@ package is the validation framework. This provides
  developers with a very high-level API for expressing form validation rules, error
  handling routines and Ajax submission processes. To inspect form data, this part of
  the package uses @YAHOO.util.Connect.setForm()@ so you will need to include that
  module in your page before using @Ojay.Forms@ for validation. Also, if you want to
  use Ajax submission you'll need the @Ojay.HTTP@ package.
  
  * @http://yui.yahooapis.com/2.6.0/build/connection/connection.js@
  * @http://yoursite.com/ojay/pkg/http.js@
  
  The best way to get a handle on how this API works is to read the "example
  implementation":/examples/validation.html. This takes you through defining a set
  of validation rules, handling errors and submitting the form with Ajax. Here we
  will cover the API and explain what each bit does.
  
  First off, note that all validation rules live inside an @Ojay.Forms()@ function
  call and a @with@ block. This block is a special environment that provides a few
  top-level helper functions that do not exist in the global namespace.
  
  The basic building block of a validation rule is the @requires()@ statement, aliased
  as @expects()@. This statement says that a field with the given @name@, in a form with
  the @id@ passed to @form()@, must be filled in before the form is allowed to submit.
  
  <pre class="prettyprint">    Ojay.Forms(function() { with(this) {
          form('login')
              .requires('username')
              .requires('password');
      }});</pre>
  
  That's all the code you need to write. Ojay will take care of intercepting @submit@
  events, extracting form data and validating it. If any errors are found, the form
  will not be allowed to submit and the list of error messages will be passed back
  to your code - we'll get on to how to handle them in due course.
  
  As well as the basic @requires()@ statement, @Ojay.Forms@ provides a few helper functions
  that implement some common validation requirements. To use them, you just call
  them after the appropriate @requires()@ or @expects()@ statement.
  
  <pre class="prettyprint">    Ojay.Forms(function() { with(this) {
          form('signup')
              .requires('username')   .toHaveLength({minimum: 6})
              .requires('email')      .toMatch(EMAIL_FORMAT)
              .requires('email_conf') .toConfirm('email');
      }});</pre>
  
  Note that @EMAIL_FORMAT@ is a constant defined for you, since it tends to come up
  pretty often in web forms. The full list of validation functions is as follows:
  
  * @toBeChecked()@ - useful for making sure checkboxes are checked.
  * @toBeNumeric()@ - makes sure the content of a field is a number.
  * @toBeOneOf(list)@ - makes sure the data matches one of the values in the array
    @list@. Comparison is performed using @==@.
  * @toBeNoneOf(list)@ - makes sure the data does not equal any of the values in
    the array @list@.
  * @toConfirm(field)@ - checks that the required field's value matches that in
    in the input with name @field@.
  * @toHaveLength(options)@ - makes sure the input has a certain length. @options@
    can be a number, or an object with @minimum@ and/or @maximum@ fields.
  * @toHaveValue(options)@ - checks that the value falls within a certain range.
    @options@ is an object with @minimum@ and/or @maximum@ fields.
  * @toMatch(regexp)@ - uses a regular expression to test the input.
  
  *Cusom error messages.* All these helper methods will generate default error messages for you. The displayed
  name for a field is inferred from its @label@, or failing that, its @name@ attribute.
  Error messages are particular to the type of validation check. Both the display names
  and error messages can be overridden with your own custom messages as follows:
  
  * To show a custom name, pass a second string argument to @requires()@/@expects()@.
  * To show a custom message, pass an additional argument to any of the validation
    methods.
  
  Some examples to illustrate:
  
  <pre class="prettyprint">    Ojay.Forms(function() { with(this) {
          
          // Gives message "User email is not valid"
          form('signup')
              .requires('userEmail').toMatch(EMAIL_FORMAT);
          
          // Gives message "Your email address is not valid"
          form('signup')
              .requires('userEmail', 'Your email address')
              .toMatch(EMAIL_FORMAT);
          
          // Gives message "User email is not a valid email address"
          form('signup')
              .requires('userEmail')
              .toMatch(EMAIL_FORMAT, 'is not a valid email address');
      }});</pre>
  
  *Custom validation routines.* If you have more specific validation rules that the above
  methods cannot handle, you can write your own custom validation methods using the
  @validates()@ statement. Within your callback, you can @get()@ data from the form and
  @add()@ errors by field name as in the "example":/examples/validation.html:
  
  <pre class="prettyprint">    form('signup').validates(function(data, errors) {
          if (/admin/i.test(data.get('username')))
              errors.add('username', 'cannot contain the phrase "admin"');
      });</pre>
  
  If you want to add an error to the form as a whole rather than a specific field, call
  @errors.addToBase(message)@ instead of @errors.add(field, message)@.
  
  *Error handling.* So now that our rules are set up, we need to do something to display
  the errors to the user. The "example":/examples/validation.html provides this snippet
  (remember, this should all go inside an @Ojay.Forms( ... )@ block):
  
  <pre class="prettyprint">    when('signup').isValidated(function(errors) {
          Ojay('#signup .error-message').remove();
          errors.forEach(function(error) {
              var field = Ojay('#signup input[name=' + error.field + ']');
              field.insert(Ojay.HTML.div({
                  className: 'error-message'
              }, error.message), 'after');
          });
      });</pre>
  
  You use @when(id).isValidated()@ to respond to failed submission attempts. @Ojay.Forms@
  hands you a list of error objects, each with a @field@ property (@null@ if the error
  comes from an @addToBase()@ call) and a @message@ property. You can loop through these
  errors and add them to your document - the above example displays each error after
  its respective field, as we know we're not using any @addToBase()@ errors here.
  
  If you just want to list the errors all in one block, we've got a helper method for
  that too: just specify a CSS selector or element reference for where you want the
  errors to be listed:
  
  <pre class="prettyprint">    when('signup').isValidated(displayErrorsIn('#errors'));</pre>
  
  This will produce markup similar to the following:
  
  <pre class="prettyprint">    <div class="error-explanation">
          <p>There were 3 errors with the form:</p>
          <ul>
              <li>Username must contain at least 6 characters</li>
              <li>Your email is not valid</li>
              <li>Password is required</li>
          </ul>
      </div></pre>
  
  h3. Ajax form submissions
  
  The other functionality provided by the @Ojay.Forms@ DSL is submission of forms
  using Ajax. Ajax-ified forms will still not submit until all their validation rules
  pass, though you don't have to validate them on the client-side if you don't want
  to. To handle the server response, use @when(id).responseArrives()@, which passes
  you an @HTTP.Response@ object just like all our @HTTP@ methods.
  
  <pre class="prettyprint">    Ojay.Forms(function() { with(this) {
          
          form('signup').submitsUsingAjax();
          when('signup').responseArrives(function(response) {
              response.insertInto('#results');
              // further response logic
          });
      }});</pre>
  
  If all you want to do is insert the response into the page, use the @displayResponseIn()@
  helper:
  
  <pre class="prettyprint">
      when('signup').responseArrives(displayResponseIn('#results'));</pre>
  
  If you want to style your form inputs, we reccommend using the styling classes detailed
  above rather than the @YAHOO.widget.Button@ family of classes. The latter require
  you to make calls to insert some hidden fields when the form is submitted and make
  repeated serialization of the form unreliable (see YUI's "known issues":http://developer.yahoo.com/yui/button/#knownissues
  for more information).
